/***************************************************************************** 
** common/oem_func.c
** 
** Copyright (c) 2009~2014 ShangHai Infotm Ltd all rights reserved. 
** 
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** 
** Description: OEM functions.
**
** Author:
**     Warits   <warits.wang@infotm.com>
**      
** Revision History: 
** ----------------- 
** 1.1  XXX 02/01/2010 XXX	Warits
*****************************************************************************/

#include <common.h>
#include <oem_func.h>
#include <oem_font.h>
#include <oem_graphic.h>
#include <oem_pic.h>
#include <asm/io.h>
#include <spi_flash.h>
#include <spi.h>
void (* oem_progress_update)(unsigned short val) = 0;
#ifdef CONFIG_MEM_TEST				//husj dbug start
int windbond_spiflash(void)
{
//	struct spi_slave *slave;
//	struct spi_flash *flash;
	char *data = (char *)0x40000000;
	char *pr = (char *)0x40000800;
	int i;
	char j = 0;
dd:
	memset(data,j++,0x800);
	memset(pr,0,0x800);
	if(oem_read_flash(pr,0,0x300)) printf("flash read error.\n");
	else for(i=0;i<0x300;i++)
			printf("%x",pr[i]);
	printf("\n");
	udelay(10000);
	if(oem_write_flash(data,0,0x300)) printf("flash write error.\n");
	else printf("flash read ok.\n");
	udelay(10000);
	if(oem_read_flash(pr,0,0x300)) printf("flash read error.\n");
	else for(i=0;i<0x300;i++)
			printf("%x",pr[i]);
	printf("\n");
	udelay(500000);
	goto dd;
/*	char W25X32[3] ={0xbf,0x30,0x16};
	spi_init();
	memset(data,0x5a,0x800);
	memset(pr,0,0x800);
	slave = spi_setup_slave(0, 0, 1000000,0);
again:	
	spi_cs_activate(slave);
	udelay(10000);
	if(slave == NULL) printf("spi setup slave err.\n");
	flash = spi_flash_probe_stmicro(slave,W25X32);
	if(flash == NULL) printf("spi flash probe windbond err.\n");
	if(spi_flash_read(flash,0,0x300,pr)) printf("flash read err.\n");
	else for(i=0;i<0x300;i++)
			printf("%x",pr[i]);
	printf("\n");
	udelay(10000);
	if(spi_flash_erase(flash,0,4096)) printf("erase flash err.\n");
	else printf("erase flash ok.\n");
	udelay(10000);
	if(spi_flash_read(flash,0,0x300,pr)) printf("flash read err.\n");
	else for(i=0;i<0x300;i++)
			printf("%x",pr[i]);
	printf("\n");
	udelay(10000);
	if(spi_flash_write(flash,0,0x300,data)) printf("write flash err\n");
	else printf("write flash ok.\n");
	udelay(10000);
	if(spi_flash_read(flash,0,0x300,pr)) printf("flash read err.\n");
	else for(i=0;i<0x300;i++)
			printf("%x",pr[i]);
	printf("\n");
	udelay(5000000);
	goto again;
*/
}

typedef u32 datum;    /* Set the data bus width to 8 bits.  */


/**********************************************************************
 *
 * Function:    memTestDataBus()
 *
 * Description: Test the data bus wiring in a memory region by
 *              performing a walking 1's test at a fixed address
 *              within that region.  The address (and hence the
 *              memory region) is selected by the caller.
 *
 * Notes:       
 *
 * Returns:     0 if the test succeeds.  
 *              A non-zero result is the first pattern that failed.
 *
 **********************************************************************/
datum memTestDataBus(volatile datum * address)
{
    datum pattern;


    /*
     * Perform a walking 1's test at the given address.
     */
    for (pattern = 1; pattern != 0; pattern <<= 1)
    {
        /*
         * Write the test pattern.
         */
        *address = pattern;

        /*
         * Read it back (immediately is okay for this test).
         */
        if (*address != pattern) 
        {
            return (pattern);
        }
  //      printf("0x%x ",pattern);
    }
//	printf("\n");
    return (0);

}   /* memTestDataBus() */



/**********************************************************************
 *
 * Function:    memTestAddressBus()
 *
 * Description: Test the address bus wiring in a memory region by
 *              performing a walking 1's test on the relevant bits
 *              of the address and checking for aliasing. This test
 *              will find single-bit address failures such as stuck
 *              -high, stuck-low, and shorted pins.  The base address
 *              and size of the region are selected by the caller.
 *
 * Notes:       For best results, the selected base address should
 *              have enough LSB 0's to guarantee single address bit
 *              changes.  For example, to test a 64-Kbyte region, 
 *              select a base address on a 64-Kbyte boundary.  Also, 
 *              select the region size as a power-of-two--if at all 
 *              possible.
 *
 * Returns:     NULL if the test succeeds.  
 *              A non-zero result is the first address at which an
 *              aliasing problem was uncovered.  By examining the
 *              contents of memory, it may be possible to gather
 *              additional information about the problem.
 *
 **********************************************************************/
datum *memTestAddressBus(volatile datum * baseAddress, unsigned long nBytes)
{
    unsigned long addressMask = (nBytes/sizeof(datum) - 1);
    unsigned long offset;
    unsigned long testOffset;

    datum pattern     = (datum) 0xAAAAAAAA;
    datum antipattern = (datum) 0x55555555;


    /*
     * Write the default pattern at each of the power-of-two offsets.
     */
    for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
    {
        baseAddress[offset] = pattern;
    }

    /* 
     * Check for address bits stuck high.
     */
    testOffset = 0;
    baseAddress[testOffset] = antipattern;

    for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
    {
        if (baseAddress[offset] != pattern)
        {
            return ((datum *) &baseAddress[offset]);
        }
    }

    baseAddress[testOffset] = pattern;

    /*
     * Check for address bits stuck low or shorted.
     */
    for (testOffset = 1; (testOffset & addressMask) != 0; testOffset <<= 1)
    {
        baseAddress[testOffset] = antipattern;

		if (baseAddress[0] != pattern)
		{
			return ((datum *) &baseAddress[testOffset]);
		}

        for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
        {
            if ((baseAddress[offset] != pattern) && (offset != testOffset))
            {
                return ((datum *) &baseAddress[testOffset]);
            }
        }

        baseAddress[testOffset] = pattern;
    }

    return (NULL);

}   /* memTestAddressBus() */


/**********************************************************************
 *
 * Function:    memTestDevice()
 *
 * Description: Test the integrity of a physical memory device by
 *              performing an increment/decrement test over the
 *              entire region.  In the process every storage bit 
 *              in the device is tested as a zero and a one.  The
 *              base address and the size of the region are
 *              selected by the caller.
 *
 * Notes:       
 *
 * Returns:     NULL if the test succeeds.
 *
 *              A non-zero result is the first address at which an
 *              incorrect value was read back.  By examining the
 *              contents of memory, it may be possible to gather
 *              additional information about the problem.
 *
 **********************************************************************/
datum *memTestDevice(volatile datum * baseAddress, unsigned long nBytes)	
{
    unsigned long offset;
    unsigned long nWords = nBytes / sizeof(datum);

    datum pattern;
    datum antipattern;


    /*
     * Fill memory with a known pattern.
     */
    for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
    {
        baseAddress[offset] = pattern;
    }

    /*
     * Check each location and invert it for the second pass.
     */
    for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
    {
        if (baseAddress[offset] != pattern)
        {
            return ((datum *) &baseAddress[offset]);
        }

        antipattern = ~pattern;
        baseAddress[offset] = antipattern;
    }

    /*
     * Check each location for the inverted pattern and zero it.
     */
    for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
    {
        antipattern = ~pattern;
        if (baseAddress[offset] != antipattern)
        {
            return ((datum *) &baseAddress[offset]);
        }
    }

    return (NULL);

}   /* memTestDevice() */

void led(char flag)
{
	u32 tmp0;
	if(flag)
	{
		tmp0 = readw(GPFDAT);
		tmp0 = tmp0 |(1<<7);
		writew(tmp0,GPFDAT);
	}
	else
	{
		tmp0 = readw(GPFDAT);
		tmp0 = tmp0 &(~(1<<7));
		writew(tmp0,GPFDAT);
	}		
}

int memTest(void)
{
	#define BASE_ADDRESS  (volatile datum *) 0x40000000
	#define NUM_BYTES     (CONFIG_SYS_SDRAM-16) * 1024 * 1024
	char info[80];
	char filename[35]={"Memory test"};
	u32 *tmp,tmp0,count = 0;
//	tmp0 = readl(GPFCON);
//	tmp0 = (tmp0 &(~(3<<14))) | (1<<14);
//	writel(tmp0,GPFCON);
//	writel(0,GPFPUD);
//	tmp0 = readw(GPFDAT);
//	tmp0 = tmp0 |(1<<7);
//	writew(tmp0,GPFDAT);

		tmp0 = memTestDataBus(BASE_ADDRESS);
		if (tmp0 != 0)
		{
			sprintf(info,"mem test data bus err :0x%x",tmp0);
			//oem_simpro_finish(info);
			printf("mem test data bus err :0x%x\n",tmp0);
			while(1);
		}
		else
		{
			sprintf(info,"mem test data bus successed");
			//oem_simpro_finish(info);
			printf("mem test data bus successed\n");
		}
		udelay(1500000);
		sprintf(info,"mem test address bus starting...");
		//oem_simpro_finish(info);
		tmp = memTestAddressBus(BASE_ADDRESS, NUM_BYTES);
		if(tmp != NULL)
		{
	    	sprintf(info,"mem test address bus err :0x%x",(u32)tmp);
			//oem_simpro_finish(info);
	    	printf("mem test Address bus err :0x%x\n",(u32)tmp);
	    	while(1);
	    }
	    else
	    {
	    	sprintf(info,"mem test address bus successed");
			//oem_simpro_finish(info);
	    	printf("mem test address bus successed\n");
	    }
	udelay(1500000);
	while(1)
	{
		sprintf(info,"%s starting...",filename);
		//oem_simpro_finish(info);
//	    udelay(500000);
	   	sprintf(info,"mem test device starting...");
		//oem_simpro_finish(info);
	    tmp = memTestDevice(BASE_ADDRESS, NUM_BYTES);
	    if( tmp != NULL)
	    {
	        sprintf(info,"%s failed! add :0x%x count:%d",filename,(u32)tmp,count);
			printf("%s failed addr: 0x%x count: %d",filename,(u32)tmp,count);
			//oem_simpro_finish(info);
			while(1);
	    }
	    else
	    {
	        count++;
	        sprintf(info,"%s successed %d.",filename,count);
			//oem_simpro_finish(info);
			printf("%s successed %d\n",filename,count);
			udelay(500000);
	    }	    
	}	
    return 0;
}
#endif 				//husj dbug end
#ifdef CONFIG_SPI_FLASH
int oem_burn_img(enum inandimage type, uint8_t *data)
{
	int  data_len=0; 
	if(!oem_check_img((unsigned int)data))
	{
		printf("oem_check_image failed");
		return -1;
	}
	
	data_len=image_get_data_size ( (image_header_t *)data) + sizeof(image_header_t);
	printf("Image length: :%d\n", data_len);

	if (type == SYSTEM ) 
	{
		data += sizeof(image_header_t );
		data_len -=  sizeof(image_header_t );

		if(oem_write_flash((char *)data, FLASH_START_ADDR_SYS, data_len)) 
			return -1;
	}  else if (type == UIMAGE) 
	{
		if(oem_write_flash((char *)data, FLASH_START_ADDR_UIMG, data_len)) 
			return -1;
	} else if (type == CONFIG) 
	{
		//data += sizeof(image_header_t );
		//data_len -=  sizeof(image_header_t );
		data_len = 0x200000-512;
		if(oem_erase_flash(FLASH_START_ADDR_CONFIG, data_len) ) 
			return -1;
	}
	return 0;

	/*else{
	}
	switch(type){
		case UIMAGE:
			ubootflag=0;
			inand_add=iNAND_START_ADDR_UIMG;
			if(!recv_len) 
				recv_len=iNAND_LEN_UIMG;
			break;
		case SYSTEM:
			ubootflag=0;
			inand_add=iNAND_START_ADDR_SYS;
			if(!recv_len) 
				recv_len=iNAND_LEN_SYS;
			break;
		case UBOOT:						//husunjie
			inand_add=0;
			recv_len = iNAND_LEN_BOOT;	//u-boot-nand.bin
			printf("vole of recv_len3:0x%x\n",recv_len);
			break;	
		default:
			return -1;
	}
	printf(" Burn Address : 0x%x\n",inand_add);	
	
	//char filename[3][16]={"uImage","system","uboot"};
	//char info[80];
	//sprintf(info,"Burning %s",filename[type]);
	//oem_simpro_init(info,recv_len);	
	if(ubootflag)
	{
		if(oem_write_flash((char *)data,inand_add,recv_len))
		{
			sprintf(info,"Burning %s failed",filename[type]);
			//oem_simpro_finish(info);
			udelay(2000000);
	       	while(1);
	       	return -1;
		}else {
				memset((char *)(data + recv_len) ,0, recv_len);
				oem_read_flash((char *)(data + recv_len) ,inand_add,recv_len);	
				temp = memcmp(data,(char *)(data + recv_len),recv_len);
				printf("memcmp temp %x\n",temp);
				if(temp)
				{
					printf("burn boot failed.\n\r");
					//oem_simpro_finish("burn boot failed, please try again.");
					while(1);					
					return -1;
				}
				sprintf(info,"Burn %s successed",filename[type]);
				printf("Burn %s successed\n",filename[type]);
				//oem_simpro_finish(info);
				udelay(500000);
				return 0;
			}
	}
	
	if(oem_write_flash((char *)data,inand_add,recv_len)){	
		sprintf(info,"Burning %s failed",filename[type]);
		//oem_simpro_finish(info);
		udelay(2000000);
	    return -1; 
	}else{
		
		memset(data,0, recv_len);
		oem_read_flash((char *)data,inand_add,recv_len);
		 if(!oem_check_img((unsigned int)data)){
			sprintf(info,"Burn %s failed",filename[type]);
			//oem_simpro_finish(info);
			udelay(2000000);
			return -1;
		 }
		sprintf(info,"Burn %s successed",filename[type]);
		//oem_simpro_finish(info);
		udelay(500000);
	}*/
	//printf("+++++++++++++++++++++++++++++++++++++++++++++++burn boot ok.\n\n\n");
	return 0;
}
/*
int oem_burn_RD(uint8_t * data)
{
	return 0;
}

int oem_burn_RD_(uint8_t * data )
{
 	return 0;
}
*/
int oem_burn_LK(uint8_t * data)
{
	return oem_burn_img( UIMAGE ,data );
}

int oem_burn_ID(uint8_t * data)
{
	if(oem_write_flash((char *)data, FLASH_START_ADDR_ID, ID_LEAN) ) 
		printf("oem_burn_ID erro\n");
	return 0;
}

int oem_burn_SYS(uint8_t * data )
{
	return	oem_burn_img(SYSTEM, data);
}
int oem_burn_UDAT(uint8_t *data ,uint32_t size)
{
	return 0;
}

int oem_burn_NDISK(uint8_t *data ,uint32_t size)
{
	return 0;
}

int oem_burn_NK(uint8_t * data )
{
	return 0;

}

int oem_burn_uboot(uint8_t * data)
{
	return oem_burn_img(UBOOT,data );		//husunjie;		//??
}

int oem_burn_zAS(uint8_t * data)
{	
	return 0;
}
int oem_burn_zSYS(uint8_t * data)
{
	int ret = 0;                                                                
           
	return ret;                                                             
}

int oem_burn_BOOTLOGO(uint8_t * data)
{
	int ret =0;
//	ret = oem_burn_img(OEM_LOGO, data, CONFIG_SYS_DISK_BOOTLG_OFFS, 
//			0 , CONFIG_SYS_BOOTLG_BURN_LEN);
	return ret;
}

int oem_burn_KERNLOGO(uint8_t * data)
{
	int ret = 0;
//	ret = oem_burn_img(OEM_LOGO, data, CONFIG_SYS_DISK_KERNLG_OFFS, 
//			0 , CONFIG_SYS_KERNLG_BURN_LEN);
	return ret;
}
#else 
static int oem_burn_img(enum oem_image_type type,
		uint8_t *data, uint32_t ndaddr, uint32_t len, uint32_t max_len)
{

	uint32_t l=0;
	switch(type)
	{
		case OEM_IMAGE_RAW:
			{
				if(SYS_TEM_PART_FORMAT==OEM_IMAGE_EXT3) break;
				//oem_simpro_init("Updating U0 ...", len * 9 / 10);
				printf("Writing data to system disk, addr=%x, size=%x\n",
						(uint32_t)data, max_len);
				oem_write_sys_disk(data, ndaddr, len , max_len);
				//oem_simpro_finish("Update U0 finished.");
				break;
			}
		case OEM_IMAGE_RAW_H:
			{
				image_header_t *hdr = (image_header_t *)data;
				char title[256];
				if(!oem_check_img((uint32_t)data))
					return -1;

				/* Erase 8MB */
				l = 0x40 + image_get_size(hdr);

				sprintf(title, "Updating image %s", image_get_name(hdr));
				printf("%s\n",title);
							
				//oem_simpro_init(title, l * 9 / 10);

				printf("Writing data to system disk,from addr=%x, to addr=%x for size=%x\n",
						(uint32_t)data, ndaddr , l);
				oem_write_sys_disk(data, ndaddr, l, max_len);
				sprintf(title, "Update image %s finished.", image_get_name(hdr));
				//oem_simpro_finish(title);
				break;
			}
		case OEM_IMAGE_YAFFS:
			{
				if(oem_write_yaffs((uint8_t *)data, len, ndaddr, max_len))
				{
					printf("Burn yaffs failed.\n");
					return -1;
				}
				printf("Burn OK.\n");
				break;
			}
		case OEM_IMAGE_EXT3:
			{
				int remain = oem_write_sys_disk(data, ndaddr, len, max_len);
				if(remain){                                    
					printf("write failed buffer: %x remain size: %x\n",(int) data , remain); 
					//oem_simpro_finish("Failed to write data to system disk for ext3.");                      
					return -1;                                                               
				}                                                                            
				oem_set_system_length(len);
				break;
			}
		case OEM_LOGO:
			if(oem_islogo_legal(data))                                 
				l = oem_getlogo_size(data);
			else if(oem_iskernlogo_legal(data)){
				l = oem_getkernlogo_size(data);
			} else {
				l = 4096;
				printf("Burning logo is damaged!!,so clear logo\n");
			}
			//oem_simpro_init("Burnning extern logo ...", l * 9 / 10);
			printf("Logo size %d\n",l);
			oem_write_sys_disk(data, ndaddr, l, max_len);
			//oem_simpro_finish("Burn extern logo finished.");

			break;
		default:
			printf("Image type %d can not be recognized.\n", type);
	}

	return 0;
}


static int oem_burn_system(uint8_t *data, uint32_t size)
{
	int ret=0;

	//oem_simpro_init("Creating system partition ...", size * 9 / 10);
	ret = oem_burn_img(SYS_TEM_PART_FORMAT , data,
			CONFIG_SYS_DISK_SYSTEM_OFFS,
			size, CONFIG_SYS_SYSTEM_BURN_LEN);
	if(!ret){
		printf("Creating success.");
		//oem_simpro_finish("Creating success.");
	}
	else {
		printf("Creating failed.");
		//oem_simpro_finish("Creating failed.");
	}
	return ret;
}
int oem_burn_zSYS(uint8_t * data)
{
	int ret;                                                                
	unsigned long l = CONFIG_SYS_PHY_AS_MAXLEN;                             
	__attribute__((noreturn)) void (*img)(void);                            


	img = oem_check_img((uint32_t)data);                                    
	if(!img)                                                                
	{                                                                       
		oem_mid2("zAS image is invalid !");                                 
		for(;;);                                                            
	}                                                                       

	oem_mid2("Extracting android data ...");                                

	if(gunzip((uint8_t *)CONFIG_SYS_PHY_AS_SWAP, CONFIG_SYS_PHY_AS_MAXLEN,  
				data + 0x40, &l))                                           
	{                                                                       
		printf("Decompress Error\n");                                       
		oem_mid2("Extracting data failed, please check your zSYS.img.");    
		for(;;);                                                            
	}                                                                       

	/* Burn System Data */                                                  
	oem_mid2("Creating file system ...");                                   
	ret = oem_burn_system((uint8_t *)CONFIG_SYS_PHY_AS_SWAP, l);               
	return ret;                                                             
}
int oem_burn_SYS(uint8_t * data )
{
	uint32_t    size;
	if(!oem_check_img((uint32_t)data)){
		return -1;
	}
	size = image_get_data_size ( (image_header_t *)data);
    
	return	oem_burn_system(data+0x40, size);
}


int oem_burn_RD(uint8_t * data)
{
	int ret;
	ret  = oem_burn_img(OEM_IMAGE_RAW_H, data, CONFIG_SYS_DISK_RD_OFFS,
			0, CONFIG_SYS_RD_BURN_LEN);
	return ret;
}

int oem_burn_RD_(uint8_t * data)
{
	int ret;
	ret  = oem_burn_img(OEM_IMAGE_RAW_H, data, CONFIG_SYS_DISK_RE_OFFS,
			0, CONFIG_SYS_RE_BURN_LEN);
	return ret;
}
int oem_burn_LK(uint8_t * data)
{
	int ret=oem_burn_img(OEM_IMAGE_RAW_H, data, CONFIG_SYS_DISK_LK1_OFFS, 0 , CONFIG_SYS_LK_BURN_LEN);
	if(!ret){
		ret=oem_burn_img(OEM_IMAGE_RAW_H, data, CONFIG_SYS_DISK_LK2_OFFS, 0 ,  CONFIG_SYS_LK_BURN_LEN);
	}
	return ret;
}
int oem_burn_BOOTLOGO(uint8_t * data)
{
	int ret;
	ret = oem_burn_img(OEM_LOGO, data, CONFIG_SYS_DISK_BOOTLG_OFFS, 
			0 , CONFIG_SYS_BOOTLG_BURN_LEN);
	return ret;
}

int oem_burn_KERNLOGO(uint8_t * data)
{
	int ret;
	ret = oem_burn_img(OEM_LOGO, data, CONFIG_SYS_DISK_KERNLG_OFFS, 
			0 , CONFIG_SYS_KERNLG_BURN_LEN);
	return ret;
}

int oem_burn_NDISK(uint8_t *data, uint32_t size)
{
	return oem_set_boot_type(BOOT_FACTORY_INIT);
}
int oem_burn_NK(uint8_t * data)
{
	int ret=0;
#if 0
	ret  = oem_burn_img(OEM_IMAGE_RAW_H, data, CONFIG_SYS_DISK_NK1_OFFS,
			0, CONFIG_SYS_DISK_NK_LEN);
	ret |= oem_burn_img(OEM_IMAGE_RAW_H, data, CONFIG_SYS_DISK_NK2_OFFS,
			0, CONFIG_SYS_DISK_NK_LEN);
#endif
	return ret;
}
int oem_burn_uboot(uint8_t * data)
{
	int ret;
	ret  = oem_burn_img(OEM_IMAGE_RAW_H, data, CONFIG_SYS_DISK_U1_OFFS,
			0, 0x300000);
	ret |= oem_burn_img(OEM_IMAGE_RAW_H, data, CONFIG_SYS_DISK_U2_OFFS,
			0, 0x300000);
	return ret;
}
#endif

int oem_load_camera_id(uint32_t ram_base)
{
	if(oem_read_flash(ram_base,FLASH_START_ADDR_ID,ID_LEAN))
		printf("oem_load_camera_id erro\n");
}
static int oem_do_load(uint32_t ram_base, uint32_t nand_addr)
{
	uint32_t l;
	image_header_t *hdr = (image_header_t *)ram_base;
//	while(1)
//		printf("++++++++++++++++++oem_do_load++++++++++++++++++++++++++\n\n");
	/* Load the first 4K */
	oem_read_flash((char *)ram_base,
			0,  0x1000);

	/* Check Magic */
	if(image_check_hcrc(hdr))
	{
		/* Load the rest OS image */
		l = 0x40 + image_get_size(hdr);
		
		oem_read_flash((char *)ram_base,
				0,  l);
	}
	else{
		printf("Wrong Image type!\n");
		return -1;
	}

	return 0;
}
int offset;
int oem_load_img(uint32_t ram_base, uint32_t nand_addr,
		uint32_t back_addr, uint32_t recv_len)
{
	__attribute__((noreturn)) void (*kernel)(void);

	/* 2: Try to load NK1 from NAND */
	printf("Try load Image from system disk 0x%x...\n",nand_addr);
retry:
	oem_do_load(ram_base, nand_addr);
	kernel = oem_check_img(ram_base);
	/* Availiable NK img, bootup winCE */
	if(kernel)
		return 0;
		if(offset){
			nand_addr+=offset*512;
			goto retry;
		}
	if(!back_addr)
		return -1;
#if 0 
	printf("Image is demaged, try to recover...\n");
	/* 3: Try to load NK2 from system disk, if Load success, renew NK1 */
	oem_do_load(ram_base, back_addr);
	kernel = oem_check_img(ram_base);
	/* Availiable NK img, bootup winCE */
	if(kernel)
	{
		/* TODO: Renew NK1 */
		uint32_t l;

		l = 0x40+image_get_size((image_header_t *)ram_base);

		printf("Recovering Image ...\n");
		oem_write_sys_disk((uint8_t *)ram_base, nand_addr,
				l, recv_len);
		return 0;
	}
#endif 
	return -1; /* Err happened while loading NK */
}
/*
 * This function check the image validity in RAM,
 * and return the pointer to real image if check passed.
 * return NULL if no available image is found.
 */
void * oem_check_img(uint32_t addr)
{
	image_header_t *header = (image_header_t *)addr;

	/* First: Check Header CRC32 */
	if(!image_check_magic(header)){
		printf("Image header invalid\n");
		return NULL;
	}
	if(!image_check_hcrc(header))
	{
		printf("Header CRC32 do not match!!\n");
		return NULL;
	}

#if 0
	/* Second: Check compress type */
	if(be32_to_cpu(header->ih_comp) != IH_COMP_NONE)
		goto __exit__;
#endif

	/* Third: Check DATA CRC32 */

	if(!image_check_dcrc(header))
	{
		printf("Data CRC32 do not match!!\n");
#if 0
		int equal=0;
		int i=0;
		int j=0;
		image_header_t *headertmp;
		while(!equal){
			addr+=128;
			headertmp=(image_header_t *)addr;
			i+=1;
			if(image_check_magic(headertmp)&&image_check_hcrc(headertmp)){
				printf("another header i=:%d\n",i);
				j=0;
				char* p=(char *)headertmp;
				char* p1=(char*)header;
				while(j<sizeof(image_header_t)){
					if(p[j]!=p1[j])break;
					j++;
				}
				if(j>=sizeof(image_header_t)){
					printf("i=%d\n",i);
					offset=i;
					equal=1;
					break;
				}

			}
			printf("**** 0x%8%x\n",addr);
			if(i>1000){ 
				offset=0;
				printf("No header found\n");
				break;
			}
		}
#endif
		return NULL;
	}

	printf("Image Ready! Load address = 0x%08x\n",
			image_get_load(header));
	return (void *)image_get_load(header);
}

#ifdef CONFIG_SYS_BOOT_NOR
int oem_burn_U0(uint8_t * data)			//nor boot
{
#if 0
	flush_cache(CONFIG_SYS_SDRAM_BASE, CONFIG_SYS_SDRAM_END);

	/* turn off I/D-cache */
	icache_disable();
	dcache_disable();
#endif

	//oem_simpro_finish("Updating U0 ...");

	nor_hw_init();
	nor_erase_chip();
#ifdef CONFIG_NOR_256K
	nor_program(0, data, 0x30000);
#else
	nor_program(0, data, 0x60000);
#endif
	//oem_simpro_finish("Update U0 finished !");
	return 0;
}
#else
int oem_burn_U0(uint8_t * data)
{
	return oem_burn_img(OEM_IMAGE_RAW, data, 0, 0x80000, 0x80000);
}
#endif

#if 0 
void oem_uboot_maintain(void)
{
	uint16_t stat = *(uint16_t *)CONFIG_SYS_PHY_BOOT_STAT;

	//printf("Uboot maintaining\n");
	if(stat == CONFIG_BOOTSTAT_U1)
	{
		printf("Boot status: U1!!\n");
		/* Normal boot */
		return;
	}
	else if(stat == CONFIG_BOOTSTAT_U2)
	{
		/* U1 is reported as invalid */
		printf("U1 is reported as invalid, checking again.\n");
#ifndef CONFIG_SYS_DISK_BLOCK
		oem_read_sys_disk((uint8_t *)CONFIG_SYS_PHY_UBOOT_SWAP, CONFIG_SYS_DISK_U1_OFFS,
				CONFIG_SYS_DISK_U_BOOT_SIZE);
		if(oem_check_img(CONFIG_SYS_PHY_UBOOT_SWAP))
		{
			printf("U1 is good!\n");
			return;
		}
		oem_read_sys_disk((uint8_t *)CONFIG_SYS_PHY_UBOOT_SWAP, CONFIG_SYS_DISK_U2_OFFS,
				CONFIG_SYS_DISK_U_BOOT_SIZE);
		oem_write_sys_disk((uint8_t *)CONFIG_SYS_PHY_UBOOT_SWAP, CONFIG_SYS_DISK_U1_OFFS,
				CONFIG_SYS_DISK_U_BOOT_SIZE, 0x300000);
#endif
#if 0
		int badblk = *(uint16_t *)(CONFIG_SYS_PHY_BOOT_STAT + 2);
		/* U1 is surely invalid, renew it */
		printf("U1 is surely invalid, mark blk %d as bad.\n", badblk);
		sprintf(cmd, "nand markbad %x", badblk * 0x80000);
		run_command(cmd, 0);
#endif
		printf("U1 rewrite OK!\n");
		return ;
	}
	else if(stat == CONFIG_BOOTSTAT_U0)
	{
		/* System is bootup through U0,
		   both u1&u2 is bad,
		   there is nothing we can do */
		printf("Boot status: U0!\n");
		return ;
	}
	else
	{
		printf("Boot status: unknown!\n");
		return ;
	}
}

#endif 
/*
 * Check u-boot and NK validity after factory burn.
 * Return value have meaning as following:
 * [31:4]	reserved.
 * [3: 2]	00: U1 & U2 is invalid.
 *			01: U1 is invalid, U2 is valid.
 *			10: U1 is valid, U2 is invalid.
 *			11: U1 & U2 is valid.
 * [1: 0]	00: NK1 & NK2 is invalid.
 *			01: NK1 is invalid, NK2 is valid.
 *			10: NK1 is valid, NK2 is invalid.
 *			11: NK1 & NK2 is valid.
 */
 #if 0 
uint32_t oem_factory_check(void)
{
	uint32_t ret = 0;
#ifndef CONFIG_SYS_DISK_BLOCK
	oem_read_sys_disk((uint8_t *)CONFIG_SYS_PHY_UBOOT_SWAP, CONFIG_SYS_DISK_U1_OFFS,
			CONFIG_SYS_DISK_U_BOOT_SIZE);
	if(oem_check_img(CONFIG_SYS_PHY_UBOOT_SWAP))
		ret |= (1 << 3);

	oem_read_sys_disk((uint8_t *)CONFIG_SYS_PHY_UBOOT_SWAP, CONFIG_SYS_DISK_U2_OFFS,
			CONFIG_SYS_DISK_U_BOOT_SIZE);
	if(oem_check_img(CONFIG_SYS_PHY_UBOOT_SWAP))
		ret |= (1 << 2);
#endif

#if defined(CONFIG_WINCE_FEATURE)
	oem_do_load(CONFIG_SYS_PHY_NK_BASE, CONFIG_SYS_DISK_NK1_OFFS);
	if(oem_check_img(CONFIG_SYS_PHY_NK_BASE))
		ret |= (1 << 1);

	oem_do_load(CONFIG_SYS_PHY_NK_BASE, CONFIG_SYS_DISK_NK2_OFFS);
	if(oem_check_img(CONFIG_SYS_PHY_NK_BASE))
		ret |= (1 << 0);
#else
	oem_do_load(CONFIG_SYS_PHY_LK_BASE, CONFIG_SYS_DISK_LK1_OFFS);
	if(oem_check_img(CONFIG_SYS_PHY_LK_BASE))
		ret |= (1 << 5);

	oem_do_load(CONFIG_SYS_PHY_LK_BASE, CONFIG_SYS_DISK_LK2_OFFS);
	if(oem_check_img(CONFIG_SYS_PHY_LK_BASE))
		ret |= (1 << 4);
#endif

	//oem_simpro_finish("Please pull out OTG line and restart.");
	return ret;
}
#endif 
#if 0 
int oem_disk_clear(void)
{
	/* FIXME: This needs to recode to match various NAND chip */
#ifdef CONFIG_WINCE_FEATURE
	return oem_erase_markbad(0x12000000, 0x6d000000);
#else
	oem_set_boot_args(BOOT_FACTORY_INIT);
	return 0;
#endif
}
#endif 
